/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
    1.Promise 就是一个类，在执行这个类的时候，需要传递一个执行器进去，执行器会立即执行
    2.Promise 有三个状态  分别是成功 fulfilled 失败 rejected 等待 pending
    pending状态可以向 成功 fulfilled 或者 失败 rejected 转变，但是成功和失败无法互相转变，
    且一旦状态确定变更成  成功或失败之后无法再进行变更
    pending->fulfilled
    pending->rejected
    3. resolve和reject 函数是用来更改状态的
    resolve:fulfilled
    reject:rejected
    4.then方法根据状态调用不同的回调方法，then方法是被定义在原型对象中的
    5.then方法成功回调和失败回调都有参数，成功回调有一个参数，表示成功之后的值，失败回调有一个参数，表示失败后的原因。
    6.同一个promise对象下面的then方法是可以被调用多次的
    7.then方法是可以被链式调用的，后面then方法的回调函数拿到的值是上一个then方法的回调函数的返回值。
    8.then方法的参数是可选的
*/
// 将状态定义为常量来进行使用
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
	constructor(executor) {
		// 使用try catch 捕获错误
		try {
			executor(this.resolve, this.reject);
		} catch (e) {
			this.reject(e);
		}
	}
	// 初始化promise状态为pending
	status = PENDING;
	// 成功之后的返回值
	value = undefined;
	// 失败之后的原因
	reason = undefined;
	// 成功回调
	successCallback = [];
	// 失败回调
	failCallback = [];
	// 该方法修改promise状态为成功
	resolve = value => {
		// 如果状态不为等待，则不修改状态
		if (this.status !== PENDING) return;
		this.status = FULFILLED;
		// 保存成功之后的值
		this.value = value;
		// 判断成功回调是否存在，存在，则调用
		// this.successCallback && this.successCallback(this.value);
		// 由于，then方法是可以进行链式调用的，所以只要当successCallback的数组长度不为0时
		// 就持续将数组内第一个回调函数弹出，并且执行。
		while (this.successCallback.length) {
			this.successCallback.shift()();
		}
	};
	// 该方法修改promise状态为失败
	reject = reason => {
		// 如果状态不为等待，则不修改状态
		if (this.status !== PENDING) return;
		this.status = REJECTED;
		// 保存失败的原因
		this.reason = reason;
		// 判断失败回调是否存在，存在，则调用
		// this.failCallback && this.failCallback(this.reason);
		while (this.failCallback.length) {
			this.failCallback.shift()();
		}
	};
	then(successCallback, failCallback) {
		// then方法可选入参处理
		successCallback = successCallback ? successCallback : value => value;
		failCallback = failCallback
			? failCallback
			: reason => {
					throw reason;
			  };
		let promise2 = new MyPromise((resolve, reject) => {
			// 判断状态调用不同的回调函数
			if (this.status === FULFILLED) {
				// 由于在resolvePromise方法中进行逻辑判断的时候，要传入promise2参数，
				// 但是在传入的时候，promise2并没有完成实例化，所以这边使用了setTimeout宏任务
				// 此处模拟使用宏任务来讲setTimeout的回调函数内容，放到同步代码之后去执行
				// 但是Promise本质上是微任务，所以此处的setTimeout并不合理
				// 更建议使用微任务 process.nextTick或者MutationObserver方法去实现
				setTimeout(() => {
					try {
						let x = successCallback(this.value);
						// 判断x的值是普通值还是promise对象
						// 如果是普通值 直接调用resolve
						// 如果是promise对象 查看promise对象返回的结果
						// 再根据promise对象返回的结果 决定调用resolve 还是reject
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			} else if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = failCallback(this.value);
						// 判断x的值是普通值还是promise对象
						// 如果是普通值 直接调用resolve
						// 如果是promise对象 查看promise对象返回的结果
						// 再根据promise对象返回的结果 决定调用resolve 还是reject
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			} else {
				// 当既不是成功也不是失败的时候，即为异步等待状态
				// 在该状态下需要将成功/失败回调存储起来
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							let x = successCallback(this.value);
							// 判断x的值是普通值还是promise对象
							// 如果是普通值 直接调用resolve
							// 如果是promise对象 查看promise对象返回的结果
							// 再根据promise对象返回的结果 决定调用resolve 还是reject
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							let x = failCallback(this.value);
							// 判断x的值是普通值还是promise对象
							// 如果是普通值 直接调用resolve
							// 如果是promise对象 查看promise对象返回的结果
							// 再根据promise对象返回的结果 决定调用resolve 还是reject
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
			}
		});
		return promise2;
	}
	// all方法将多个任务  一起执行完成后，在调用promise的回调方法
	static all(array) {
		let result = [];
		let index;
		return new MyPromise((resolve, reject) => {
			function addData(key, value) {
				result[key] = value;
				index++;
				if (index === array.length) {
					resolve(result);
				}
			}
			for (let i = 0; i < array.length; i++) {
				let current = array[i];
				if (current instanceof MyPromise) {
					// promise对象
					current.then(
						value => addData(i, value),
						reason => reject(reason)
					);
				} else {
					// 普通值
					addData(i, array[i]);
				}
			}
			resolve(result);
		});
	}
	// resolve方法
	static resolve(value) {
		if (value instanceof MyPromise) return value;
		return new MyPromise(resolve => resolve(value));
	}
	// finally方法
	finally(callback) {
		// 由于finally中可以返回promise，所以要等回调函数callback执行完成后
		// 再去返回value，所以使用MyPromise下的静态方法resolve来实现
		return this.then(
			value => {
				return MyPromise.resolve(callback()).then(() => value);
			},
			reason => {
				return MyPromise.resolve(callback()).then(() => {
					throw reason;
				});
			}
		);
	}
	// catch方法 本质上就是调用了promise的then方法 只是没有调用then方法中的成功回调，只有失败回调
	catch(failCallback) {
		return this.then(undefined, failCallback);
	}
}

function resolvePromise(promise2, x, resolve, reject) {
	// 当return出去的promise和自身相等时，会出现循环调用，导致报错
	if (promise2 === x) {
		reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
	}
	if (x instanceof MyPromise) {
		// promise对象
		x.then(resolve, reject);
	} else {
		// 普通值
		resolve(x);
	}
}

module.exports = MyPromise;
